package com.idea.relax.tool.core;

import java.lang.reflect.Array;
import java.util.*;

/**
 * @className: SqlSplicer
 * @description: 依据布尔条件拼接SQL语句的工具类
 * <p>
 * 1.满足boolean条件就去拼接这个SQL片段;
 * 2.假如不传入boolean条件的参数,默认判断变量的条件是：不等于 null,不等于 " ",不等于 0 ;
 * 3.joinIn方法是专门用于拼接 IN() SQL语句的;
 * 4.getSql()方法可以获取到最终拼接完毕的SQL语句
 * 5.getParams()方法可以获取SQL语句中有效的参数数组,顺序和SQL语句中占位符的顺序一致
 * </p>
 * @author: salad
 * @date: 2021/7/27
 * 1.轻量
 * 2.支持In
 * 3.支持链式调用
 **/
public class SqlSplicer {


    private final StringBuilder sql;

    private Object[] params = {};

    private static final String IN = "IN";

    private static final String WHERE = "WHERE";

    private static final String ZERO = "0";

    private static final String EMPTY = " ";

    private static final String BR = "\r\n";

    private static final String LPTHS = "(";

    private static final String RPTHS = ")";

    private static final String COMMA = ",";

    private static final String QM = "?";

    private SqlSplicer(StringBuilder sql) {
        this.sql = sql;
    }


    public SqlSplicer(StringBuilder sql, Object[] params) {
        this.sql = sql;
        this.params = params;
    }


    public static SqlSplicer of(String initSql) {
        return new SqlSplicer(new StringBuilder(initSql));
    }

    public static SqlSplicer of(String sql, Object... params) {
        return new SqlSplicer(new StringBuilder(sql), params);
    }


    public SqlSplicer join(String append, Object param) {
        if (!isEmpty(param)) {
            sql.append(EMPTY).append(erasureAnd(sql, append));
            params = Arrays.copyOf(params, params.length + 1);
            params[params.length - 1] = param;
        }
        return this;
    }

    public SqlSplicer join(String append, boolean isJoining, Object param) {
        if (isJoining) {
            sql.append(EMPTY).append(erasureAnd(sql, append));
            params = Arrays.copyOf(params, params.length + 1);
            params[params.length - 1] = param;
        }
        return this;
    }


    public SqlSplicer joinAny(String append, Object... params) {
        if (deepValidationArray(params)) {
            sql.append(EMPTY).append(erasureAnd(sql, append));
            this.params = concatAll(this.params, params);
        }
        return this;
    }

    public SqlSplicer joinCustom(String append, boolean isJoining, Object... params) {
        if (isJoining) {
            sql.append(EMPTY).append(erasureAnd(sql, append));
            this.params = concatAll(this.params, params);
        }
        return this;
    }

    public SqlSplicer joinIn(String append, Object... params) {

        StringBuilder sbf = erasureAnd(sql, append);

        int mark = 0;

        String s = append.trim().toUpperCase();

        if (!s.endsWith(IN)) {
            sbf.append(EMPTY).append(IN);
        }

        for (int i = 0; i < params.length; i++) {

            if (!isEmpty(params[i])) {

                mark++;
                sbf.append(EMPTY);

                if (mark == 1)
                    sbf.append(LPTHS);

                sbf.append(QM);
                sbf.append(COMMA);
                params = Arrays.copyOf(params, params.length + 1);
                params[params.length - 1] = params[i];
            }

        }

        if (mark > 0)
            sbf.deleteCharAt(sbf.length() - 1);

        sbf.append(RPTHS);

        if (mark > 0)
            sql.append(EMPTY).append(sbf);

        return this;
    }

    public SqlSplicer br() {
        getSql().append(BR);
        return this;
    }


    public StringBuilder getSql() {
        if (sql == null || sql.length() == 0)
            throw new IllegalArgumentException("SQL is empty :[" + sql + "]");
        erasureWhere();
        return sql;
    }

    public String getSqlStr() {
        return getSql().toString();
    }

    public Object[] getParams() {
        return params;
    }

    private boolean isEmpty(Object obj) {
        if (obj == null) {
            return true;
        } else if (obj instanceof Optional) {
            return !((Optional<?>) obj).isPresent();
        } else if (obj instanceof CharSequence) {
            return ((CharSequence) obj).length() == 0;
        } else if (obj.getClass().isArray()) {
            return Array.getLength(obj) == 0;
        } else if (obj instanceof Collection) {
            return ((Collection<?>) obj).isEmpty();
        } else if (obj instanceof Number) {
            return ZERO.equals(obj.toString());
        } else {
            return obj instanceof Map && ((Map<?, ?>) obj).isEmpty();
        }
    }

    private boolean deepValidationArray(Object... params) {
        if (params == null || Array.getLength(params) == 0)
            return false;
        for (Object param : params) {
            if (isEmpty(param)) {
                return false;
            }
        }
        return true;
    }

    private Object[] concatAll(Object[] first, Object[] rest) {
        Object[] r = Arrays.copyOf(first, first.length + rest.length);
        System.arraycopy(rest, 0, r, first.length, rest.length);
        return r;
    }

    private StringBuilder erasureAnd(StringBuilder front, String next) {
        StringBuilder strBuilder = new StringBuilder(next.trim());
        if (lastFiveStr(front).toUpperCase().equals(WHERE))
            strBuilder.delete(0, 3);
        return strBuilder;
    }


    private void erasureWhere() {
        if (params.length == 0) { //假如没有拼接任何参数，需要擦除SQL的WHERE子句
            if (lastFiveStr(sql).toUpperCase().equals(WHERE))
                sql.delete((sql.length() - WHERE.length()) - 1, sql.length() - 1);
        }
    }

    private String lastFiveStr(StringBuilder src) {
        String str = src.toString().trim();
        return str.substring(str.length() - 5);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (!(obj instanceof SqlSplicer)) {
            return false;
        }

        SqlSplicer other = (SqlSplicer) obj;
        return Objects.equals(sql, other.sql);
    }

    @Override
    public int hashCode() {

        return sql != null ? sql.hashCode() : 0;
    }

    @Override
    public String toString() {
        return "sql: " + sql.toString() + BR + "params: " + Arrays.toString(params);
    }

//demo
//    public static void main(String[] args) {
//        int userId = 123456;
//        String name = null;
//        String start = "0x1113";
//        String end = "0x1113";
//        test(userId,name,start,end);
//    }
//    //满足boolean条件就去拼接这个SQL片段;
//    //假如不传入boolean条件的参数,默认判断变量的条件是：不等于 null,不等于 " ",不等于 0 ;
//    //joinIn方法是专门用于拼接 IN() SQL语句的;
//    public static void  test(int userid, String name,String start,String end){
//    	SqlSplicer sqlJoiner = SqlSplicer.of("select * from usertable  where","a",1,"");
//    	SqlSplicer join = sqlJoiner
//                .join("and userid = ?",  userid != 0,userid).br()
//                .join("and name like ?",name!=null&&name.trim().length()>0,"%"+name)
//                .joins("and date >= ? and date <= ?",start,end)
//                .joinIn("and code", 1,2,3);
//        System.out.println(join);
//    }


}
